﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Reflection;
using HqlIntellisense.ConfigurationAbstraction;
using NHibernate.Cfg;

namespace HQLAddin.ConfigurationInspector
{
	public class ConfigurationLookup : MarshalByRefObject
	{
		private IEnumerable<IPersistentClassInformation> classes = new List<IPersistentClassInformation>();
		private IDictionary<string, string> imports = new Dictionary<string, string>();
		private IEnumerable<ConfigurationError> errors;

		public ConfigurationLookup(string path)
		{
			SearchConfiguration(path);
		}

		private void SearchConfiguration(string path)
		{
			var basePath = (string)AppDomain.CurrentDomain.GetData("APPBASE");

			using (var catalog = new DirectoryCatalog(path))
			using (var container = new CompositionContainer(catalog))
			{
				container.Compose(new CompositionBatch());
				Type configType = NhTypes.Configuration;
				try
				{
					AppDomain.CurrentDomain.SetData("APPBASE", path);
					if (configType != null)
					{
						var configs = container.GetExportedValues(configType);
						if (configs != null)
						{
							Initialize(configs);
						}
					}
					else
					{
						var configs = container.GetExportedValues<Configuration>();
						Initialize(configs);
					}
					AppDomain.CurrentDomain.SetData("APPBASE",basePath);
				}
				catch(TargetInvocationException ex)
				{
					Exception mostInner = ex;
					var compositionException = ex.InnerException as CompositionException;
					if(compositionException != null)
					{
						mostInner = compositionException.Errors.First().Exception;
					}
					while (mostInner.InnerException != null)
					{
					    mostInner = mostInner.InnerException;
					}
					errors = new List<ConfigurationError> { new ConfigurationError { Description = mostInner.Message } };
				}
				catch (Exception ex)
				{
					var mostInner = ex;
					while (mostInner.InnerException != null) 
					{
						mostInner = mostInner.InnerException;   
					}
					errors = new List<ConfigurationError> {new ConfigurationError {Description = mostInner.Message}};
				}
				finally 
				{
					AppDomain.CurrentDomain.SetData("APPBASE",basePath);
				}
			}
			return;
		}

		private void Initialize(IEnumerable<dynamic> configs)
		{
			foreach (var config in configs)
			{
				var newClasses = DynamicMapper.GetClasses(config);
				var newImports = config.Imports;

				classes = ConfigurationHelper.CombineClassInformation(classes, newClasses);
				imports = ConfigurationHelper.CombineImports(imports, newImports);
			}
		}

		public IEnumerable<IPersistentClassInformation> GetClasses()
		{
			return classes;
		}

		public IDictionary<string, string> GetImports()
		{
			return imports;
		}

		public IEnumerable<ConfigurationError> GetErrors()
		{
			return errors ?? Enumerable.Empty<ConfigurationError>();
		}
	}
}